package com.dx.mes.common.core.dao.impl;

import com.dx.mes.common.bean.entity.HisXml;
import com.dx.mes.common.bean.entity.TableHistoryConfig;
import com.dx.mes.common.bean.entity.UUIDObject;
import com.dx.mes.common.core.dao.GenericDao;
import com.dx.mes.common.core.redis.RedisManager;
import com.dx.mes.common.transcoder.util.*;
import org.apache.commons.betwixt.io.BeanWriter;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

//import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW )
@Repository
public class GenericHibernateDao<T extends Serializable, PK extends Serializable> extends HibernateDaoSupport
        implements GenericDao<T, PK> {
    private static final Logger logger = Logger.getLogger(RedisManager.class);
    private static final String OPERATION_UPDATE = "UPDATE";
    private static final String OPERATION_INSERT = "INSERT";
    private static final String OPERATION_DELETE = "DELETE";
    private static final String OPERATION_SAVE_OR_UPDATE = "IorU";
    private static final String OPERATION_SAVE_OR_UPDATE_ALL = "IorUAL";

    private Class<T> entityClass;

    @Resource
    private RedisManager redisManager;

    @Resource
    public void setSessionFactoryOverride(SessionFactory sessionFactory) {
        super.setSessionFactory(sessionFactory);
    }

    public GenericHibernateDao() {
        this.entityClass = null;
        Class c = getClass();
        Type t = c.getGenericSuperclass();
        if (t instanceof ParameterizedType) {
            Type[] p = ((ParameterizedType) t).getActualTypeArguments();
            this.entityClass = (Class<T>) p[0];
        }
    }

    //获取redis中的配置档
    public TableHistoryConfig getTableHistoryConfig(String tableName) {
        Jedis jedis = redisManager.getJedis();
        TableHistoryConfig tableConfig = new TableHistoryConfig();
        try {
            String redisKey = "lodhis_config_" + tableName;
            byte[] bs = jedis.get(redisKey.getBytes());
            if (bs != null) {
//                tableConfig = (TableHistoryConfig) ObjectTranscoder.deserialize(bs);
                tableConfig = (TableHistoryConfig)  JacksonUtil.fromJson(new String(bs),TableHistoryConfig.class);
                if (tableConfig == null) {
                    tableConfig = new TableHistoryConfig();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            redisManager.returnResource(jedis);//释放redis实例
        }
        return tableConfig;
    }

    public void sendHistoryMessageToRedis(Object entity) {
        String tableName = entity.getClass().getSimpleName().toUpperCase();
        if (StringUtils.isEmpty(tableName)) {
            return;
        }

        //获取配置，是否需要发送his Q
        TableHistoryConfig tableHistoryConfig = getTableHistoryConfig(tableName);
        if (tableHistoryConfig.isHasToSendHis()) {
            logger.info("send info to redis start:");
            String xmlInfo = this.hisQueryReport(entity);
            if (xmlInfo != null) {
                //将xml保存到redis中
                Jedis jedis = redisManager.getJedis();
                List<HisXml> hisXmlList = new ArrayList<HisXml>();
                try {
                    String redisKey = "tempData_for_MQ_" + AppContext.getCurrEventNumber();
                    byte[] bs = jedis.get(redisKey.getBytes());
                    if (bs != null) {
                        String redisValue = new String(bs);
//                        hisXmlList = (List<HisXml>) ObjectTranscoder.deserialize(bs);
                        hisXmlList = (List<HisXml>) JacksonUtil.fromJson(redisValue,List.class);
                    } else {
                        hisXmlList = new ArrayList<HisXml>();
                    }
                    hisXmlList.add(new HisXml(tableHistoryConfig, xmlInfo));
//                    jedis.setex(redisKey.getBytes(), 300 ,ObjectTranscoder.serialize(hisXmlList));
//                    jedis.setex(redisKey.getBytes(), 300 , JacksonUtil.toJSONStr(hisXmlList).getBytes("UTF-8"));
                    logger.info("Send Redis's Key:"+redisKey);
                    jedis.setex(redisKey, 300 , JacksonUtil.toJSONStr(hisXmlList));
                    logger.info("send info to redis end:");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    redisManager.returnResource(jedis);//释放redis实例
                }
            }
        }
    }

    public void sendHistoryMessageToRedisNew(Collection<T> entities) {
        String tableName = entities.iterator().next().getClass().getSimpleName().toUpperCase();
        /*String tableName="";
		for(Object T:entities){
			 tableName = T.getClass().getSimpleName().toUpperCase();
			 break;
		}*/
        //String tableName = entities.getClass().getSimpleName().toUpperCase();
        System.out.println(tableName);
        if (StringUtils.isEmpty(tableName)) {
            return;
        }

        //获取配置，是否需要发送his Q
        TableHistoryConfig tableHistoryConfig = getTableHistoryConfig(tableName);
        if (tableHistoryConfig.isHasToSendHis()) {

            String xmlInfo = this.hisQueryReportNew(entities);
            if (xmlInfo != null) {
                //将xml保存到redis中
                Jedis jedis = redisManager.getJedis();
                List<HisXml> hisXmlList = new ArrayList<HisXml>();
                try {
                    String redisKey = "tempData_for_MQ_" + AppContext.getCurrEventNumber();
                    byte[] bs = jedis.get(redisKey.getBytes());
                    if (bs != null) {
                        hisXmlList = (List<HisXml>) ObjectTranscoder.deserialize(bs);
                    } else {
                        hisXmlList = new ArrayList<HisXml>();
                    }
                    hisXmlList.add(new HisXml(tableHistoryConfig, xmlInfo));
                    jedis.set(redisKey.getBytes(), ObjectTranscoder.serialize(hisXmlList));
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    redisManager.returnResource(jedis);//释放redis实例
                }
            }
        }
    }

    public String hisQueryReport(Object inObj) {
        StringWriter sw = new StringWriter();
        sw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><transaction><trans_cnt>1</trans_cnt>");
        String replacedOutXml = null;
        BeanWriter beanWriter = new BeanWriter(sw);
        beanWriter.setEndOfLine("");
        beanWriter.getXMLIntrospector().getConfiguration().setAttributesForPrimitives(false);
        beanWriter.getBindingConfiguration().setMapIDs(false);
        try {
            beanWriter.write("table", inObj);

            sw.write("</transaction>");
            replacedOutXml = StringUtils.replaceEach(sw.toString(), new String[]{"<id>", "</id>"}, new String[]{"", ""});

        } catch (Exception e) {
            logger.error(e.toString());
        } finally {
            try {
                sw.close();
            } catch (Exception e) {
                logger.error(e.toString());
            }
        }
        return replacedOutXml;
    }

    public String hisQueryReportNew(Collection<T> entities) {
        StringWriter sw = new StringWriter();
        sw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><transaction><trans_cnt>" + entities.size() + "</trans_cnt>");
        String replacedOutXml = null;
        BeanWriter beanWriter = new BeanWriter(sw);
        beanWriter.setEndOfLine("");
        beanWriter.getXMLIntrospector().getConfiguration().setAttributesForPrimitives(false);
        beanWriter.getBindingConfiguration().setMapIDs(false);
        try {
            for (Object entity : entities) {
                beanWriter.write("table", entity);
            }
            sw.write("</transaction>");
            replacedOutXml = StringUtils.replaceEach(sw.toString(), new String[]{"<id>", "</id>"}, new String[]{"", ""});
        } catch (Exception e) {
            logger.error(e.toString());
        } finally {
            try {
                sw.close();
            } catch (Exception e) {
                logger.error(e.toString());
            }
        }
        System.out.println(replacedOutXml);
        return replacedOutXml;
    }

    public T get(PK id) {
        if (id == null) {
            return null;
        }
        return (T) getHibernateTemplate().get(entityClass, id);
    }

    public T getWithLock(PK id, LockMode lock) {
        if (id == null) {
            return null;
        }
        T t = (T) getHibernateTemplate().get(entityClass, id, lock);
        if (t != null) {
            this.flush(); // 立即刷新，否则锁不会生效。
        }
        return t;
    }

    public T load(PK id) {
        return (T) getHibernateTemplate().load(entityClass, id);
    }

    public T loadWithLock(PK id, LockMode lock) {
        T t = (T) getHibernateTemplate().load(entityClass, id, lock);
        if (t != null) {
            this.flush(); // 立即刷新，否则锁不会生效。
        }
        return t;
    }

    public List<T> loadAll() {
        return (List<T>) getHibernateTemplate().loadAll(entityClass);
    }

    private void setEntityCommonProperty(T entity, String operation) {
        ((UUIDObject) entity).setPv_unq_seq_id(((UUIDObject) entity).getUnq_seq_id());
        ((UUIDObject) entity).setUnq_seq_id(GUIDGenerator.javaGUID());
        ((UUIDObject) entity).setOperation(operation);
        ((UUIDObject) entity).setOpe_tbl_name(entity.getClass().getSimpleName().toUpperCase());
        ((UUIDObject) entity).setSta_time_d(Util.getDoubleTime());
        ((UUIDObject) entity).setOpe_evt_name(AppContext.getCurrServiceName());
        ((UUIDObject) entity).setOpe_evt_node(LocalUtil.getLocalInfo().getIp());
        ((UUIDObject) entity).setOpe_evt_usr(LocalUtil.getLocalInfo().getHostName());
        ((UUIDObject) entity).setOpe_evt_no(AppContext.getCurrEventNumber());
    }

    private void setEntityCommonPropertyNew(Collection<T> entities, String operation) {
        for (T entity : entities) {
            ((UUIDObject) entity).setPv_unq_seq_id(((UUIDObject) entity).getUnq_seq_id());
            ((UUIDObject) entity).setUnq_seq_id(GUIDGenerator.javaGUID());
            ((UUIDObject) entity).setOperation(operation);
            ((UUIDObject) entity).setOpe_tbl_name(entity.getClass().getSimpleName().toUpperCase());
            ((UUIDObject) entity).setSta_time_d(Util.getDoubleTime());
            ((UUIDObject) entity).setOpe_evt_name(AppContext.getCurrServiceName());
            ((UUIDObject) entity).setOpe_evt_node(LocalUtil.getLocalInfo().getIp());
            ((UUIDObject) entity).setOpe_evt_usr(LocalUtil.getLocalInfo().getHostName());
            ((UUIDObject) entity).setOpe_evt_no(AppContext.getCurrEventNumber());
        }

    }
    public void update(T entity) {
        long beginTime = System.currentTimeMillis();
        setEntityCommonProperty(entity, OPERATION_UPDATE);
        getHibernateTemplate().update(entity);
        flushCurSession();
        long redis_begin_time = System.currentTimeMillis();
        this.sendHistoryMessageToRedis(entity);
        long endTime = System.currentTimeMillis();
        logger.info("send his use time:"+(endTime - redis_begin_time));
        logger.info("update table:"+entity.getClass().getSimpleName()+",use time:"+(endTime-beginTime));
    }

    public void update2(T entity) {
        setEntityCommonProperty(entity, OPERATION_UPDATE);
        getHibernateTemplate().update(entity);
        this.sendHistoryMessageToRedis(entity);
    }

    public void save(T entity) {
        long beginTime = System.currentTimeMillis();
        setEntityCommonProperty(entity, OPERATION_INSERT);
        getHibernateTemplate().save(entity);
        flushCurSession();
        long redis_begin_time = System.currentTimeMillis();
        this.sendHistoryMessageToRedis(entity);
        long endTime = System.currentTimeMillis();
//        logger.info("send his use time:"+(endTime - redis_begin_time));
//        logger.info("save table:"+entity.getClass().getSimpleName()+",use time:"+(endTime-beginTime));
    }

    public void saveOrUpdate(T entity) {
        setEntityCommonProperty(entity, OPERATION_SAVE_OR_UPDATE);
        getHibernateTemplate().saveOrUpdate(entity);
        flushCurSession();
        this.sendHistoryMessageToRedis(entity);
    }

    public void saveOrUpdate2(T entity) {
        setEntityCommonProperty(entity, OPERATION_SAVE_OR_UPDATE);
        getHibernateTemplate().saveOrUpdate(entity);
        this.sendHistoryMessageToRedis(entity);
    }

    public void saveOrUpdateAll(Collection<T> entities) {
//        getHibernateTemplate().saveOrUpdateAll(entities);
        entities.forEach(entity -> {
            getHibernateTemplate().saveOrUpdate(entity);
            flushCurSession();
            setEntityCommonPropertyNew(entities, OPERATION_SAVE_OR_UPDATE_ALL);
            this.sendHistoryMessageToRedisNew(entities);
        });

		/*for (T entity : entities) {
			setEntityCommonProperty(entity, OPERATION_SAVE_OR_UPDATE_ALL);
			this.sendHistoryMessageToRedis(entity);
		}*/
    }

    public void delete(T entity) {
        setEntityCommonProperty(entity, OPERATION_DELETE);
        getHibernateTemplate().delete(entity);
        flushCurSession();
        this.sendHistoryMessageToRedis(entity);
    }

    public void deleteByKey(PK id) {
        this.delete(this.load(id));
    }

    public void deleteAll(Collection<T> entities) {
        getHibernateTemplate().deleteAll(entities);
        flushCurSession();
        setEntityCommonPropertyNew(entities, OPERATION_DELETE);
        this.sendHistoryMessageToRedisNew(entities);
		/*for (T entity : entities) {
			setEntityCommonProperty(entity, OPERATION_DELETE);
			this.sendHistoryMessageToRedis(entity);
		}*/
    }

    public List<T> find(String queryString) {
//        return getHibernateTemplate().find(queryString);
        final List<?> objects = getHibernateTemplate().find(queryString);
        return objects.stream().map(o->(T)o).collect(Collectors.toList());
    }

    public List<T> findWithLock(String queryString) {
        final List<?> objects = getHibernateTemplate().find(queryString);
        return objects.stream().map(o->(T)o).collect(Collectors.toList());
    }

    @SuppressWarnings("rawtypes")
    public List<T> find(String queryString, Object values) {
        return getHibernateTemplate().find(queryString, values).stream().map(o->(T)o).collect(Collectors.toList());
    }

    @SuppressWarnings("rawtypes")
    public List find(String queryString, Object[] values) {
        return getHibernateTemplate().find(queryString, values);
    }


    public void lock(T entity, LockMode lock) {
        getHibernateTemplate().lock(entity, lock);
    }

    public void flush() {
        getHibernateTemplate().flush();
    }

    public List listBySQL(String sql) {
        Session session = getCrSession();
        return session.createSQLQuery(sql).list();
    }

    public List findBySQL(String sql) {
        Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query q = s.createSQLQuery(sql);
        return q.list();
    }

    @SuppressWarnings("unchecked")
    public List<T> findBySQL(String sql, Class clazz) {
        Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query q = s.createSQLQuery(sql).addEntity(clazz);
        return q.list();
    }

    @SuppressWarnings("unchecked")
    public List<T> findBySQL(String sql, Class clazz, Object... args) {
        Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query q = s.createSQLQuery(sql).addEntity(clazz);
        return q.list();
    }

    public T findBySQLOnly(String sql) {
        Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query q = s.createSQLQuery(sql);
        return (T) q.uniqueResult();
    }

    public List<String> findDistStringList(final String strSql) {
        Query query = getCrSession().createQuery(strSql);
        return query.list();
    }

    public T comTblRead(String sql) {
        Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query q = s.createQuery(sql);
        Object obj = q.uniqueResult();
        return (T) obj;
    }

    public T comTblReadLock(String sql, String tbl_name) {
        Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query q = s.createQuery(sql);
        q.setLockMode(tbl_name, LockMode.READ);
        Object obj = q.uniqueResult();
        return (T) obj;
    }

    public List comTblRead2nd(String sql) {
        Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query q = s.createQuery(sql);
        return q.list();
    }

    public T findOnlyOne(String queryString, Object[] values) {
        return ((List<T>) getHibernateTemplate().find(queryString, values)).get(0);
    }

    public Session getCrSession() {
        return getHibernateTemplate().getSessionFactory().getCurrentSession();
    }

    public void flushCurSession() {
        Session curSession = this.getCrSession();
        curSession.flush();
    }

    public long getCountBySql(String sql) {
        return (Long) getHibernateTemplate().iterate(sql).next();
    }

    @SuppressWarnings("unchecked")
    public List<T> list(String hql, Object... values) {
        Query query = this.createQuery(hql, values);
        return query.list();
    }

    @SuppressWarnings("unchecked")
    public T uniqueResult(String hql, Object... values) {
        Query query = this.createQuery(hql, values);
        return (T) query.uniqueResult();
    }

    protected Query createQuery(String hql, Object... values) {
        Query query = getCrSession().createQuery(hql);

        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query;
    }

    @SuppressWarnings("unchecked")
    public List<T> listWithLock(String hql, Object... values) {
        Session session = this.getCrSession();
        Query query = this.createQuery(hql, values);
        List<T> ts = query.list();
        for (T t : ts) {
            session.lock(t, LockMode.UPGRADE_NOWAIT);
        }
        return ts;
    }
    public List<T> listWithLock(Query query) {
        Session session = this.getCrSession();
//        Query query =session.createQuery(hql);
//        query.setParameterList(0,valueList);
        List<T> ts = query.list();
        for (T t : ts) {
            session.lock(t, LockMode.UPGRADE_NOWAIT);
        }
        return ts;
    }

    @SuppressWarnings("unchecked")
    public T uniqueResultWithLock(String hql, Object... values) {
        Session session = this.getCrSession();
        Query query = this.createQuery(hql, values);
        T t = (T) query.uniqueResult();
        if (t == null) {
            return t;
        }
        session.lock(t, LockMode.UPGRADE_NOWAIT);
        return t;
    }
}
